package com.h117121.fashionshop.core.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.h117121.fashionshop.core.dao.AccountDao;
import com.h117121.fashionshop.core.dao.InvoiceDao;
import com.h117121.fashionshop.core.dao.ProducerDao;
import com.h117121.fashionshop.core.dao.ProductDao;
import com.h117121.fashionshop.core.dao.ProductDao.AndOrQueryType;
import com.h117121.fashionshop.core.dao.ProductInInvoiceDao;
import com.h117121.fashionshop.core.dao.ProductTypeDao;
import com.h117121.fashionshop.core.dom.Account;
import com.h117121.fashionshop.core.dom.Account.AccountStatus;
import com.h117121.fashionshop.core.dom.Account.Sex;
import com.h117121.fashionshop.core.dom.Account.UserType;
import com.h117121.fashionshop.core.dom.Cart;
import com.h117121.fashionshop.core.dom.Invoice;
import com.h117121.fashionshop.core.dom.Invoice.InvoiceState;
import com.h117121.fashionshop.core.dom.Invoice.PaymentMethod;
import com.h117121.fashionshop.core.dom.Producer;
import com.h117121.fashionshop.core.dom.Product;
import com.h117121.fashionshop.core.dom.ProductInCart;
import com.h117121.fashionshop.core.dom.ProductQuery;
import com.h117121.fashionshop.core.dom.ProductType;
import com.h117121.fashionshop.core.dom.ResultPage;
import com.h117121.fashionshop.core.exception.AccountNotFoundException;
import com.h117121.fashionshop.core.exception.BannedAccountException;
import com.h117121.fashionshop.core.exception.CartEmptyException;
import com.h117121.fashionshop.core.exception.EntityNotFoundException;
import com.h117121.fashionshop.core.exception.SoldOutProductException;
import com.h117121.fashionshop.core.exception.UsernameExistException;
import com.h117121.fashionshop.core.exception.WrongPasswordException;
import com.h117121.fashionshop.core.tool.Utils;

@Transactional
@Service("businessService")
public class BusinessServiceImple implements BusinessService {
	@Autowired
	private ProducerDao producerDao;
	@Autowired
	private ProductTypeDao productTypeDao;
	@Autowired
	private ProductDao productDao;
	@Autowired
	private AccountDao accountDao;
	@Autowired
	private InvoiceDao invoiceDao;
	@Autowired
	private ProductInInvoiceDao productInInvoiceDao;

	@Override
	public List<Producer> getAllProducer() {
		return producerDao.getAll();
	}

	@Override
	public List<ProductType> getAllProductType() {
		return productTypeDao.getAll();
	}

	@Override
	public ResultPage getMostSoldProducts(int index, int n) {
		return productDao.getMostSoldProducts(index, n);
	}

	@Override
	public ResultPage getNewestProducts(int index, int n) {
		return productDao.getNewestProducts(index, n);
	}

	@Override
	public ResultPage findProductByKeyword(String keyword, int index,
			int numItems) {
		return productDao.getByKeyword(keyword, index, numItems);
	}

	@Override
	public ResultPage findByProductQuery(ProductQuery query, int index,
			int numItems) {
		return productDao.findByProductQuery(query, AndOrQueryType.AND, index,
				numItems);
	}

	@Override
	public Product getProduct(long id) throws EntityNotFoundException {
		return productDao.findById(id);
	}

	@Override
	public ResultPage getSimilarProducts(long id, int index, int numItems) {
		return productDao.getSimilarProducts(id, index, numItems);
	}

	/* Login */
	@Override
	public Account authenticate(String username, String password)
			throws AccountNotFoundException, WrongPasswordException,
			BannedAccountException {
		Account account = accountDao.findByUsername(username);
		if (account.getStatus() == AccountStatus.BANNED)
			throw new BannedAccountException();
		if (account.getUserPassword().equals(password)) {
			return account;
		} else {
			throw new WrongPasswordException();
		}
	}

	/* Register */
	@Override
	public void register(Account account) throws UsernameExistException {
		try {
			accountDao.findByUsername(account.getUserName());
			throw new UsernameExistException();
		} catch (AccountNotFoundException ex) {
			account.setDateRegistered(new Date());
			account.setStatus(AccountStatus.ACTIVE);
			account.setUserType(UserType.REGISTERED);
			account.setSex(Sex.UNKNOWN);

			accountDao.saveOrUpdate(account);
		}
	}

	/* Update */
	@Override
	public Account updateAccount(Account account)
			throws EntityNotFoundException {
		Account acc = accountDao.findById(account.getId());
		acc.updateInfo(account);
		accountDao.saveOrUpdate(acc);
		return acc;
	}

	@Override
	public Account updateAccountAdmin(Account account)
			throws EntityNotFoundException {
		Account acc = accountDao.findById(account.getId());
		acc.update(account);
		accountDao.saveOrUpdate(acc);
		return acc;
	}

	@Override
	@Transactional
	public void checkoutCart(Account account, PaymentMethod paymentMethod)
			throws CartEmptyException, SoldOutProductException {
		Cart cart = account.getCart();
		if (cart == null) {
			throw new CartEmptyException();
		}

		List<Product> list = new ArrayList<Product>();

		for (ProductInCart item : cart.getItems()) {
			if (item.getProduct().getQuantity() - item.getQuantity() < 0) {
				throw new SoldOutProductException();
			}

			item.getProduct().setQuantity(
					item.getProduct().getQuantity() - item.getQuantity());
			item.getProduct().setAmountSold(
					item.getProduct().getAmountSold() + item.getQuantity());
			productDao.saveOrUpdate(item.getProduct());
			for (int i = 0; i < item.getQuantity(); i++) {
				list.add(item.getProduct());
			}
		}

		Invoice invoice = Utils.convertToInvoice(cart, account, new Date(),
				null, paymentMethod, InvoiceState.WAITING);
		invoiceDao.saveOrUpdate(invoice);
	}

	@Override
	public void raiseView(Product product) {
		product.setViews(product.getViews() + 1);
		productDao.saveOrUpdate(product);
	}

	@Override
	public void updateProduct(Product product) throws EntityNotFoundException {
		if (product.getId() > 0) {
			productDao.saveOrUpdate(productDao.findById(product.getId()).copy(
					product));
		} else {
			productDao.saveOrUpdate(product);
		}
	}

	@Override
	public void updateProducer(Producer producer) {
		producerDao.saveOrUpdate(producer);
	}

	@Override
	public void updateProductType(ProductType productType) {
		productTypeDao.saveOrUpdate(productType);
	}

	@Override
	public void deleteProduct(Product product) {
		productDao.delete(product);
	}

	@Override
	public void deleteProducer(Producer producer) {
		producerDao.delete(producer);
	}

	@Override
	public void deleteProductType(ProductType productType) {
		productTypeDao.delete(productType);

	}

	@Override
	public void deleteAccount(Account account) {
		accountDao.delete(account);
	}

	@Override
	public ResultPage getAllProduct(int index, int numItems) {
		return productDao.getAll(index, numItems);
	}

	@Override
	public ResultPage getAllProducer(int index, int numItems) {
		return producerDao.getAll(index, numItems);
	}

	@Override
	public ResultPage getAllProductType(int index, int numItems) {
		return productTypeDao.getAll(index, numItems);
	}

	@Override
	public ResultPage getAllAccount(int index, int numItems) {
		return accountDao.getAll(index, numItems);
	}

	@Override
	public void banAccount(Account account) {
		account.setStatus(AccountStatus.BANNED);
		accountDao.saveOrUpdate(account);
	}

	@Override
	public void unbanAccount(Account account) {
		account.setStatus(AccountStatus.ACTIVE);
		accountDao.saveOrUpdate(account);
	}

	@Override
	public ResultPage findProduct(String keyword, int index, int numItems) {
		return productDao.getByKeyword(keyword, index, numItems);
	}

	@Override
	public ResultPage findProducer(String keyword, int index, int numItems) {
		return producerDao.getByKeyword(keyword, index, numItems);
	}

	@Override
	public ResultPage findProductType(String keyword, int index, int numItems) {
		return productTypeDao.getByKeyword(keyword, index, numItems);
	}

	@Override
	public ResultPage findAccount(String keyword, int index, int numItems) {
		return accountDao.getByKeyword(keyword, index, numItems);
	}

	@Override
	public Producer getProducer(long id) throws EntityNotFoundException {
		return producerDao.findById(id);
	}

	@Override
	public ProductType getProductType(long id) throws EntityNotFoundException {
		return productTypeDao.findById(id);
	}

	@Override
	public Account getAccount(long id) throws EntityNotFoundException {
		return accountDao.findById(id);
	}

	@Override
	public List<Product> getRandomProducts(int numItems) {
		return productDao.getRandomProducts(numItems);
	}

	@Override
	public Account findByUsername(String username) throws AccountNotFoundException {
		return accountDao.findByUsername(username);
	}
}
